#include "VxIrrLib.h"

//============================================================================
//=== constructor ===//
GameInputBase::GameInputBase()
	: m_poIrrBase(0)
	, m_bInitialized(0)
	, m_bMouseLocked(0)
	, m_iMouseX(0)
	, m_iMouseY(0)
	//=== orientation vars ===//
	, m_v3fPhoneOrientation(0.0f, 0.0f, 0.0f )
	//=== touch event vars ===//
	, m_u32TouchStartMs(0)		// start time in ms of touch
	, m_f32TouchDnX(0.0f)			// x position of touch down event
	, m_f32TouchDnY(0.0f)			// y position touch down event start 
	, m_f32TouchUpX(0.0f) 			// x position of touch up event
	, m_f32TouchUpY(0.0f) 			// y position of touch up event
	, m_bIsLongPress(0)			// if true is a long press
	, m_f32ScaledTouchSlopSquared(0.0f) // touch distance slop based on screen size
	, m_bTouchHasNotMoved(0.0f) 	// if true touch was not moved from start ( except within slop distance )
	, m_poTouchSceneNode(0)		// scene node touched during touch down event
{
	//=== key vars ===//
	memset( m_abKeys, 0, sizeof( m_abKeys ) );
	memset( m_abMouseButState, 0, sizeof( m_abMouseButState ) );
}

//============================================================================
//=== destructor ===//
GameInputBase::~GameInputBase()
{
}
//============================================================================
//! startup
void GameInputBase::GameInputStartup( VxIrrBase * poIrrBase )
{
	m_poIrrBase = poIrrBase;
}
//============================================================================
//! shutdown
void GameInputBase::GameInputShutdown( void )
{
}
//============================================================================
//=== methods ===//
//! reset all inputs
void GameInputBase::ResetAllInput( void )
{
	m_bMouseLocked = 0;
	m_iMouseX = 0;
	m_iMouseY = 0;
	//=== orientation vars ===//
	m_v3fPhoneOrientation.set( 0.0f, 0.0f, 0.0f );
	//=== touch event vars ===//
	m_u32TouchStartMs = 0;		// start time in ms of touch
	m_f32TouchDnX = 0.0f;			// x position of touch down event
	m_f32TouchDnY = 0.0f;			// y position touch down event start 
	m_f32TouchUpX = 0.0f; 			// x position of touch up event
	m_f32TouchUpY = 0.0f; 			// y position of touch up event
	m_bIsLongPress = 0;			// if true is a long press
	m_f32ScaledTouchSlopSquared = 0.0f; // touch distance slop based on screen size
	m_bTouchHasNotMoved = 0.0f; 	// if true touch was not moved from start ( except within slop distance )
	m_poTouchSceneNode = 0;		// scene node touched during touch down event

	//=== key vars ===//
	memset( m_abKeys, 0, sizeof( m_abKeys ) );
	memset( m_abMouseButState, 0, sizeof( m_abMouseButState ) );
}
//============================================================================
//! return text description of key
const char * GameInputBase::GetKeyName( irr::EKEY_CODE eKey )
{
	switch( eKey )
	{
	case irr::KEY_LBUTTON:		return "Left mouse button";
	case irr::KEY_RBUTTON:		return "Right mouse button";
	case irr::KEY_CANCEL:		return "Control-break processing";
	case irr::KEY_MBUTTON:		return "Middle mouse button (three-button mouse)";
	case irr::KEY_XBUTTON1:		return "Windows 2000/XP: X1 mouse button";
	case irr::KEY_XBUTTON2:		return "Windows 2000/XP: X2 mouse button";
	case irr::KEY_BACK:			return "BACKSPACE";
	case irr::KEY_TAB:			return "TAB";
	case irr::KEY_CLEAR:		return "CLEAR";
	case irr::KEY_RETURN:		return "ENTER";
	case irr::KEY_SHIFT:		return "SHIFT";
	case irr::KEY_CONTROL:		return "CTRL";
	case irr::KEY_MENU:			return "ALT";
	case irr::KEY_PAUSE:		return "PAUSE";
	case irr::KEY_CAPITAL:		return "CAPS LOCK";
	//case irr::KEY_KANA             = 0x15,  // IME Kana mode
	//case irr::KEY_HANGUEL          = 0x15,  // IME Hanguel mode (maintained for compatibility use KEY_HANGUL)
	//case irr::KEY_HANGUL           = 0x15,  // IME Hangul mode
	//case irr::KEY_JUNJA            = 0x17,  // IME Junja mode
	//case irr::KEY_FINAL            = 0x18,  // IME final mode
	//case irr::KEY_HANJA            = 0x19,  // IME Hanja mode
	//case irr::KEY_KANJI            = 0x19,  // IME Kanji mode
	case irr::KEY_ESCAPE:		return "ESC";
	//case irr::KEY_CONVERT          = 0x1C,  // IME convert
	//case irr::KEY_NONCONVERT       = 0x1D,  // IME nonconvert
	//case irr::KEY_ACCEPT           = 0x1E,  // IME accept
	//case irr::KEY_MODECHANGE       = 0x1F,  // IME mode change request
	case irr::KEY_SPACE:		return "SPACEBAR";
	case irr::KEY_PRIOR:		return "PAGE UP";
	case irr::KEY_NEXT:			return "PAGE DOWN";
	case irr::KEY_END:			return "END";
	case irr::KEY_HOME:			return "HOME";
	case irr::KEY_LEFT:			return "LEFT ARROW";
	case irr::KEY_UP:			return "UP ARROW";
	case irr::KEY_RIGHT:		return "RIGHT ARROW";
	case irr::KEY_DOWN:			return "DOWN ARROW";
	case irr::KEY_SELECT:		return "SELECT";
	case irr::KEY_PRINT:		return "PRINT";
	case irr::KEY_EXECUT:		return "EXECUTE";
	case irr::KEY_SNAPSHOT:		return "PRINT SCREEN";
	case irr::KEY_INSERT:		return "INS";
	case irr::KEY_DELETE:		return "DEL";
	case irr::KEY_HELP:			return "HELP";
	case irr::KEY_KEY_0:		return "0";
	case irr::KEY_KEY_1:		return "1";
	case irr::KEY_KEY_2:		return "2";
	case irr::KEY_KEY_3:		return "3";
	case irr::KEY_KEY_4:		return "4";
	case irr::KEY_KEY_5:		return "5";
	case irr::KEY_KEY_6:		return "6";
	case irr::KEY_KEY_7:		return "7";
	case irr::KEY_KEY_8:		return "8";
	case irr::KEY_KEY_9:		return "9";
	case irr::KEY_KEY_A:		return "A";
	case irr::KEY_KEY_B:		return "B";
	case irr::KEY_KEY_C:		return "C";
	case irr::KEY_KEY_D:		return "D";
	case irr::KEY_KEY_E:		return "E";
	case irr::KEY_KEY_F:		return "F";
	case irr::KEY_KEY_G:		return "G";
	case irr::KEY_KEY_H:		return "H";
	case irr::KEY_KEY_I:		return "I";
	case irr::KEY_KEY_J:		return "J";
	case irr::KEY_KEY_K:		return "K";
	case irr::KEY_KEY_L:		return "L";
	case irr::KEY_KEY_M:		return "M";
	case irr::KEY_KEY_N:		return "N";
	case irr::KEY_KEY_O:		return "O";
	case irr::KEY_KEY_P:		return "P";
	case irr::KEY_KEY_Q:		return "Q";
	case irr::KEY_KEY_R:		return "R";
	case irr::KEY_KEY_S:		return "S";
	case irr::KEY_KEY_T:		return "T";
	case irr::KEY_KEY_U:		return "U";
	case irr::KEY_KEY_V:		return "V";
	case irr::KEY_KEY_W:		return "W";
	case irr::KEY_KEY_X:		return "X";
	case irr::KEY_KEY_Y:		return "Y";
	case irr::KEY_KEY_Z:		return "Z";
	//case irr::KEY_LWIN             = 0x5B,  // Left Windows" (Microsoft Natural keyboard)
	//case irr::KEY_RWIN             = 0x5C,  // Right Windows" (Natural keyboard)
	//case irr::KEY_APPS             = 0x5D,  // Applications" (Natural keyboard)
	//case irr::KEY_SLEEP            = 0x5F,  // Computer Sleep"
	case irr::KEY_NUMPAD0:		return "keypad 0";
	case irr::KEY_NUMPAD1:		return "keypad 1";
	case irr::KEY_NUMPAD2:		return "keypad 2";
	case irr::KEY_NUMPAD3:		return "keypad 3";
	case irr::KEY_NUMPAD4:		return "keypad 4";
	case irr::KEY_NUMPAD5:		return "keypad 5";
	case irr::KEY_NUMPAD6:		return "keypad 6";
	case irr::KEY_NUMPAD7:		return "keypad 7";
	case irr::KEY_NUMPAD8:		return "keypad 8";
	case irr::KEY_NUMPAD9:		return "keypad 9";
	case irr::KEY_MULTIPLY:		return "Multiply";
	case irr::KEY_ADD:			return "Add";
	case irr::KEY_SEPARATOR:	return "Separator";
	case irr::KEY_SUBTRACT:		return "Subtract";
	case irr::KEY_DECIMAL:		return "Decimal";
	case irr::KEY_DIVIDE:		return "Divide";
	case irr::KEY_F1:			return "F1";
	case irr::KEY_F2:			return "F2";
	case irr::KEY_F3:			return "F3";
	case irr::KEY_F4:			return "F4";
	case irr::KEY_F5:			return "F5";
	case irr::KEY_F6:			return "F6";
	case irr::KEY_F7:			return "F7";
	case irr::KEY_F8:			return "F8";
	case irr::KEY_F9:			return "F9";
	case irr::KEY_F10:			return "F10";
	case irr::KEY_F11:			return "F11";
	case irr::KEY_F12:			return "F12";
	//case irr::KEY_F13:				return "F13"
	//case irr::KEY_F14:				return "F14"
	//case irr::KEY_F15:				return "F15"
	//case irr::KEY_F16:				return "F16"
	//case irr::KEY_F17              = 0x80,  // F17"
	//case irr::KEY_F18              = 0x81,  // F18"
	//case irr::KEY_F19              = 0x82,  // F19"
	//case irr::KEY_F20              = 0x83,  // F20"
	//case irr::KEY_F21              = 0x84,  // F21"
	//case irr::KEY_F22              = 0x85,  // F22"
	//case irr::KEY_F23              = 0x86,  // F23"
	//case irr::KEY_F24              = 0x87,  // F24"
	case irr::KEY_NUMLOCK:		return "NUM LOCK";
	case irr::KEY_SCROLL:		return "SCROLL LOCK";
	case irr::KEY_LSHIFT:		return "Left SHIFT";
	case irr::KEY_RSHIFT:		return "Right SHIFT";
	case irr::KEY_LCONTROL:		return "Left CONTROL";
	case irr::KEY_RCONTROL:		return "Right CONTROL";
	case irr::KEY_LMENU:		return "Left MENU";
	case irr::KEY_RMENU:		return "Right MENU";
	case irr::KEY_PLUS:			return "Plus Key   (+)";
	case irr::KEY_COMMA:		return "Comma Key  (,)";
	case irr::KEY_MINUS:		return "Minus Key  (-)";
	case irr::KEY_PERIOD:		return "Period Key (.)";
	//case irr::KEY_ATTN             = 0xF6,  // Attn"
	//case irr::KEY_CRSEL            = 0xF7,  // CrSel"
	//case irr::KEY_EXSEL            = 0xF8,  // ExSel"
	//case irr::KEY_EREOF            = 0xF9,  // Erase EOF"
	//case irr::KEY_PLAY             = 0xFA,  // Play"
	//case irr::KEY_ZOOM             = 0xFB,  // Zoom"
	//case irr::KEY_PA1              = 0xFD,  // PA1"
	case irr::KEY_OEM_CLEAR:	return "Clear";
	}
	return "UNKNOWN KEY";
}
//============================================================================
//=== overrides ===//
//! override irrlicht event handler
//! irrlicht passes all events through this handler, however, if
//! the window is a external window none of the events are generated and
//! also the android implementation has more event types than known by irrlicht 
//! so we override then call the HandleXxxxxEvent that will be used in all implementations
bool GameInputBase::OnEvent( const irr::SEvent& oIrrEvent )
{
	//log_msg( 0, "GameInputBase::OnEvent: oIrrEvent %d\n", oIrrEvent );

	switch( oIrrEvent.EventType ) 
	{
	case irr::EET_LOG_TEXT_EVENT:
		//log_msg( 0, oIrrEvent.LogEvent.Text );
		break;
	case irr::EET_KEY_INPUT_EVENT:
		if( oIrrEvent.KeyInput.PressedDown )
		{
			return HandleKeyEvent( eKeyEventDown, oIrrEvent.KeyInput.Key );
		}
		else
		{
			return HandleKeyEvent( eKeyEventUp, oIrrEvent.KeyInput.Key );
		}
		break;
	case irr::EET_MOUSE_INPUT_EVENT:
		switch( oIrrEvent.MouseInput.Event ) 
		{
		case irr::EMIE_LMOUSE_PRESSED_DOWN:
			//log_msg( 0, "GameInputBase::OnMouseEvent: lbut down, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonLeft, eMouseEventDown, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
		case irr::EMIE_RMOUSE_PRESSED_DOWN:
			//log_msg( 0, "GameInputBase::OnMouseEvent: rbut down, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonRight, eMouseEventDown, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
		case irr::EMIE_MMOUSE_PRESSED_DOWN:
			//log_msg( 0, "GameInputBase::OnMouseEvent: mbut down, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonMiddle, eMouseEventDown, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			break;
		case irr::EMIE_LMOUSE_LEFT_UP:
			//log_msg( 0, "GameInputBase::OnMouseEvent: lbut up, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonLeft, eMouseEventUp, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
		case irr::EMIE_RMOUSE_LEFT_UP:
			//log_msg( 0, "GameInputBase::OnMouseEvent: rbut up, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonRight, eMouseEventUp, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
		case irr::EMIE_MMOUSE_LEFT_UP:
			//log_msg( 0, "GameInputBase::OnMouseEvent: mbut up, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonMiddle, eMouseEventUp, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			break;
		case irr::EMIE_MOUSE_MOVED:
			//log_msg( 0, "GameInputBase::OnMouseEvent: mouse move, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseEvent( eMouseButtonNone, eMouseEventMove, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			break;
		case irr::EMIE_MOUSE_WHEEL:
			//log_msg( 0, "GameInputBase::OnMouseEvent: mouse wheel, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			return OnMouseWheel( oIrrEvent.MouseInput.Wheel );
		break;
		default:
			log_msg( 0, "GameInputBase::OnMouseEvent: UNKNOWN, X=%d, Y=%d\n", oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
			break;
		}
		return false;
		break;
	case irr::EET_GUI_EVENT:
		//log_msg( 0, "GameInputBase::OnGuiEvent: event type %d, caller %d X=%d, Y=%d\n", oIrrEvent.GUIEvent.EventType, oIrrEvent.GUIEvent.Caller, oIrrEvent.MouseInput.X, oIrrEvent.MouseInput.Y );
		return OnGuiEvent( oIrrEvent.GUIEvent.EventType, oIrrEvent.GUIEvent.Caller );
		break;
	default:
		break;
	}
	return false;
}
//============================================================================
//! handle input from orientation sensor
bool GameInputBase::HandleOrientationEvent( F32 f32RotX, F32 f32RotY, F32 f32RotZ  )
{
	//log_msg( 0, "GameInputBase::HandleOrientationEvent: X=%3.2f, Y=%3.2f, Z=%3.2f\n", f32RotX, f32RotY, f32RotZ );

	m_v3fPhoneOrientation.set( f32RotX, f32RotY, f32RotZ  );
	return OnOrientationEvent( f32RotX, f32RotY, f32RotZ  );
}
//============================================================================
//! handle input from touch sensor or mouse
bool GameInputBase::HandleMouseEvent( EMouseButtonType eMouseButType, EMouseEventType eMouseEventType, int iMouseXPos, int iMouseYPos  )
{
	// we post mouse events through the irrlicht posting system instead of calling OnMouseEvent
	// so that the irrlicht gui events are generated
	m_iMouseX = iMouseXPos;
	m_iMouseY = iMouseYPos;
	//log_msg( 0, "GameInputBase::HandleMouseEvent: but %d event %d X=%d Y=%d\n", eMouseButType, eMouseEventType, m_iMouseX, m_iMouseY );

	/*

	if( m_bMouseLocked && 
		( eMouseEventMove == eMouseEventType ) ) 
	{
		// handle locking cursor to center of screen
		irr::core::position2df pos2fMouseUpdate = m_poIrrBase->m_poDevice->getCursorControl()->getRelativePosition();
		if( false == ( irr::core::equals( pos2fMouseUpdate.X, 0.5f ) && irr::core::equals( pos2fMouseUpdate.Y, 0.5f )))
		{
			m_poIrrBase->m_poDevice->getCursorControl()->setPosition(0.5f, 0.5f);						
		}
		return OnMouseEvent(	eMouseButType, 
								eMouseEventType, 
								(int)((pos2fMouseUpdate.X - 0.5f) * m_poIrrBase->m_poDriver->getScreenSize().Width * 0.1f * GameConfigImp::Instance().m_f32MouseScaleX),
								(int)((pos2fMouseUpdate.Y - 0.5f) * m_poIrrBase->m_poDriver->getScreenSize().Height * 0.1f * GameConfigImp::Instance().m_f32MouseScaleY ));
	}
	return OnMouseEvent( eMouseButType, eMouseEventType, iMouseXPos, iMouseYPos  );
	*/
	irr::SEvent oIrrEvent;
	oIrrEvent.EventType = irr::EET_MOUSE_INPUT_EVENT;
	oIrrEvent.MouseInput.X = m_iMouseX;
	oIrrEvent.MouseInput.Y = m_iMouseY;
	switch( eMouseEventType )
	{
	case eMouseEventMove:
		//log_msg( 0, "GameInputBase::HandleMouseEvent: Moved X=%d Y=%d\n", m_iMouseX, m_iMouseY );
		oIrrEvent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
		break;
	case eMouseEventDown:
		//log_msg( 0, "GameInputBase::HandleMouseEvent: Down X=%d Y=%d\n", m_iMouseX, m_iMouseY );
		oIrrEvent.MouseInput.Event = irr::EMIE_LMOUSE_PRESSED_DOWN;
		break;
	case eMouseEventUp:
		//log_msg( 0, "GameInputBase::HandleMouseEvent: Up X=%d Y=%d\n", m_iMouseX, m_iMouseY );
		oIrrEvent.MouseInput.Event = irr::EMIE_LMOUSE_LEFT_UP;
		break;
	default:
		//log_msg( 0, "GameInputBase::HandleMouseEvent: UNKNOW X=%d Y=%d\n", m_iMouseX, m_iMouseY );
		oIrrEvent.MouseInput.Event = irr::EMIE_MOUSE_MOVED;
		break;
	}
	//log_msg( 0, "GameInputBase::HandleMouseEvent: Posting Event\n" );
	return m_poIrrBase->m_poDevice->postEventFromUser( oIrrEvent );
}
//============================================================================
//! called after processed HandleMouseWheel for derived classes to override
bool GameInputBase::HandleMouseWheel( F32 f32MouseWheelDist )
{
	return OnMouseWheel( f32MouseWheelDist );
}

//============================================================================
//! handle input from touch sensor or mouse
bool GameInputBase::HandleKeyEvent( EKeyEventType eKeyEventType, irr::EKEY_CODE eKey, int iFlags )
{
	return OnKeyEvent( eKeyEventType, eKey, iFlags );
}

//============================================================================
//! handle event from irrlicht gui
bool GameInputBase::HandleGuiEvent( irr::gui::EGUI_EVENT_TYPE eGuiEventType, irr::gui::IGUIElement * poGuiElement )
{
	return OnGuiEvent( eGuiEventType, poGuiElement );
}

//============================================================================
//! handle input from touch sensor or mouse for derived classes to override
bool GameInputBase::HandleInputAction( EInputActionType eInputActionType, F32 f32PosX, F32 f32PosY, F32 f32PosZ  )
{
	return OnInputAction( eInputActionType, f32PosX, f32PosY, f32PosZ  );
}
//============================================================================
//! handle input from screen touch
bool GameInputBase::HandleTouchEvent( int iEvent, F32 f32PosX, F32 f32PosY )
{
	//log_msg( 0, "GameInputBase::HandleTouchEvent: Posting Event\n" );
	switch( iEvent )
	{
	case E_TOUCH_ACTION_DOWN:
		//log_msg( 0, "GameInputBase::HandleTouchEvent: lbut down, X=%3.2f, Y=%3.2f\n", f32PosX, f32PosY );
		return OnMouseEvent( eMouseButtonLeft, eMouseEventDown, (int)f32PosX, (int)f32PosY  );
	case E_TOUCH_ACTION_MOVE:
		//log_msg( 0, "GameInputBase::HandleTouchEvent: move, X=%3.2f, Y=%3.2f\n", f32PosX, f32PosY );
		return OnMouseEvent( eMouseButtonNone, eMouseEventMove, (int)f32PosX, (int)f32PosY  );
	//E_TOUCH_ACTION_UP		= 1,
	case E_TOUCH_ACTION_UP:
		//log_msg( 0, "GameInputBase::HandleTouchEvent: lbut up, X=%3.2f, Y=%3.2f\n", f32PosX, f32PosY );
		return OnMouseEvent( eMouseButtonLeft, eMouseEventUp, (int)f32PosX, (int)f32PosY  );
	default:
		log_msg( 0, "GameInputBase::HandleTouchEvent: UNKNOWN EVENT TYPE\n" );
	}
	return false;
}

//============================================================================
//! handle pause game
void GameInputBase::HandlePause()
{
	log_msg( 0, "GameInputBase::HandlePause: \n" );
	OnPause();
}

//============================================================================
//! handle resume game
void GameInputBase::HandleResume()
{
	log_msg( 0, "GameInputBase::HandleResume: \n" );
	OnResume();
}

//============================================================================
//! handle restart game
void GameInputBase::HandleRestart()
{
	log_msg( 0, "GameInputBase::HandleRestart: \n" );
	OnRestart();
}


